﻿using HtmlAgilityPack;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace HobSoft.SoccerBet2014.Domain.Common.Site
{
    using Component;
    using Domain.Models.Site;
    using Domain.Models;
    using Domain.DataService;
    using SiteContext;
    using SoccerBet2014.Common.Json;

    public partial class IbcSessionManager : SessionManagerBase
    {
        public IbcSessionManager(ICookiesManager cookieManager, IBetGameDataService repositoryService)
            : base(cookieManager, repositoryService)
        {

        }

        public override void Initialize(string globalHost, byte bookerId, string account)
        {
            base.Initialize(globalHost, bookerId, account);

            LiveFormParamters = new OddsDataFormParamters();
            NoLiveFormParamters = new OddsDataFormParamters();
            EarlyFormParamters = new OddsDataFormParamters();
            FormRequestExtKey = Common.SiteContext.ContextManager.Register(bookerId, account, "global", "ExtKey");
            FormRequestExtValue = Common.SiteContext.ContextManager.Register(bookerId, account, "global", "ExtValue");
        }

        public IbcSessionManager(ICookiesManager cookieManager, IBetGameDataService repositoryService, string globalHost, byte bookerId, string account)
            : base(cookieManager, repositoryService, globalHost, bookerId, account)
        {
        }

        protected string HtmlMain
        {
            get;
            set;
        }

        public override async Task<bool> Start()
        {
            if (!ValidInitialize())
                return false;

            Uri url = new Uri(string.Format("http://www.{0}/default.aspx", GlobalHost));
            Cookie chCookie = new Cookie("LangKey", "ch");
            CookieContainer cookies = new CookieContainer();
            cookies.Add(url, chCookie);
            var resTask = GetHtmlRequest(url, cookies, "");
            HtmlMain = await resTask;
            if (resTask.Exception != null || resTask.IsCanceled) return false;
            return true;
        }

        public override Task RequestSignupToken()
        {
            throw new NotImplementedException();
        }

        public override Task ChangeLauguage()
        {
            throw new NotImplementedException();
        }

        public override async Task<bool> Signup(string password, string token)
        {
            if (!ValidInitialize() && string.IsNullOrEmpty(HtmlMain))
                return false;

            string content = string.Empty;
            try
            {
                HtmlDocument htmlDoc = new HtmlDocument();
                htmlDoc.LoadHtml(HtmlMain);
                var loginForm = htmlDoc.DocumentNode.SelectSingleNode("//form[@name='frmLogin']");
                var formNodes = loginForm.SelectNodes("//input");
                HtmlFormHelper formHelper = new HtmlFormHelper(formNodes);

                string code = string.Empty;
                if (!string.IsNullOrEmpty(token))
                {
                    formHelper.ChangeValueByName("txtCode", token);
                    code = token;
                }
                else
                {
                    var codeNode = formHelper.InputNodes.FirstOrDefault(n => n.Name == "txtCode");
                    if (codeNode != null)
                        code = codeNode.Value;
                }
                var encryptPasswordString = Md5EncryptPassword(password, code);
                formHelper.ChangeValueByName("txtID", Account);
                formHelper.ChangeValueByName("txtPW", encryptPasswordString);

                content = "selLang=ch&" + formHelper.ToString();
            }
            catch (Exception ex)
            {
                LoginState = LoginState.LoginError;
                throw new Exception("创建用户登录提交发生异常", ex);
            }


            string loginUrl = string.Format("http://www.{0}/ProcessLogin.aspx", GlobalHost);
            string loginReferer = string.Format("http://www.{0}/Default.aspx", GlobalHost);
            CookiesManager.DeleteCookies(BookerID, Account, SessionID, new string[] { string.Format("www.{0}", GlobalHost) });
            var reqSigninTask = PostHtmlRequest(new Uri(loginUrl), loginReferer, content, false);
            var htmlLoginResult = await reqSigninTask;

            if (!CheckLoginHtmlState(htmlLoginResult))
            {
                LoginState = LoginState.LoginError;
                return false;
            }
            string validHtml = string.Empty;
            if (htmlLoginResult.Contains("rulesalert.aspx"))
            {
                validHtml = await RequestRuleAlert();
            }
            else if (htmlLoginResult.Contains("ValidateTicket.aspx"))
            {
                validHtml = htmlLoginResult;
            }
            else
            {
                LoginState = LoginState.LoginError;
                return false;
            }

            string validUrl = string.Empty;
            var matValid = regValid.Match(validHtml);
            if (matValid.Success)
            {
                validUrl = matValid.Groups["u"].Value;
            }
            else
            {
                LoginState = LoginState.LoginError;
                return false;
            }
            if (string.IsNullOrEmpty(validUrl))
            {
                LoginState = LoginState.LoginError;
                return false;
            }

            Uri validUri = new Uri(validUrl);
            Host = validUri.Host;
            var reqValid = GetHtmlRequest(validUri, loginUrl, false);
            var htmlLastSignup = await reqValid;

            var sessionCookie = CookiesManager.GetCookie(BookerID, Account, LoginInfo.SessionID, new Uri(string.Format("http://{0}", Host)));
            var sessionValue = sessionCookie != null ? sessionCookie.Value : string.Empty;
            _loginInfo.ResetLogin(sessionValue);
            LoginState = LoginState.Logined;
            return true;
        }

        protected virtual async Task<string> RequestRuleAlert()
        {
            string alertUrl = string.Format("http://www.{0}/rulesalert.aspx", GlobalHost);
            string htmlRuleAlert = string.Empty;
            try
            {
                Uri url = new Uri(alertUrl);
                var cookies = CookiesManager.GetCookies(BookerID, Account, url);
                var request = CreateRequest(url, cookies, HTML_MIME, alertUrl, "Accept=YES", "POST");
                request.ContentType = "application/x-www-form-urlencoded";
                var resTask = request.GetResponseAsync();
                HttpWebResponse response = (HttpWebResponse)await resTask;
                htmlRuleAlert = ReadResponseStream(response);
                CookiesManager.SetCookies(BookerID, Account, response.Cookies);
                response.Close();
            }
            catch (Exception ex)
            {
                LoginState = LoginState.LoginError;
                throw ex;
            }
            return htmlRuleAlert;
        }

        public override async Task<string> RequestMainPage()
        {
            if (!ValidSignup()) return string.Empty;
            return await RequestTodayMain();
        }

        public virtual async Task<string> RequestTodayMain()
        {
            if (!ValidSignup()) return string.Empty;

            string referer = string.Format("http://{0}/main.aspx", Host);
            string underOverUrl = string.Format("http://{0}/UnderOver.aspx?Sport=1&Market=t&DispVer=new&Game=0", Host);

            var uoreqTask = GetHtmlRequest(new Uri(underOverUrl), referer);
            var html = await uoreqTask;
            if (string.IsNullOrEmpty(html) || uoreqTask.IsCanceled || uoreqTask.Exception != null)
                return string.Empty;

            ResolveTodayFormParamters(html);
            RequestTodayTime = new DateTime();
            _loginInfo.Reset(html);
            return html;
        }

        protected virtual bool ValidTodayRequest()
        {
            if (!RequestTodayTime.HasValue) return false;
            var span = DateTime.Now - RequestTodayTime.Value;
            return (span.TotalSeconds < TodayTimeout);
        }


        #region request order
        public override async Task<IList<BettingOrder>> RequestBettingHisOrder()
        {
            if (!ValidSignup() && ValidTodayRequest()) return null;
            string dataUrl = string.Format("http://{0}/Betlist.aspx?from=full", Host);
            string referer = string.Format("http://{0}/topmenu.aspx", Host);
            var reqTask = GetHtmlRequest(new Uri(dataUrl), referer);
            var html = await reqTask;
            if (reqTask.IsCanceled || reqTask.Exception != null)
                return null;
            _loginInfo.Reset(html);
            return ResolveHtmlHisOrder(html);
        }

        protected virtual IList<BettingOrder> ResolveHtmlHisOrder(string html)
        {
            List<BettingOrder> orders = new List<BettingOrder>();
            HtmlDocument doc = new HtmlDocument();
            doc.LoadHtml(html);
            var betInfoNodes = doc.DocumentNode.SelectNodes("//table[contains(@class,'oddsTable')]/tr[position()>1]");
            if (betInfoNodes == null)
                return null;
            foreach (var betInfoNode in betInfoNodes)
            {
                var teamNode = betInfoNode.SelectSingleNode("td[3]//div[contains(@class,'TextStyle04')][1]");
                var leagueNode = betInfoNode.SelectSingleNode("td[3]//span[contains(@class,'TextStyle04')]");
                var oddsIdNode = betInfoNode.SelectSingleNode("td[2]/div[1]/strong");
                var timeNode = betInfoNode.SelectSingleNode("td[2]/div[2]");
                var statusNode = betInfoNode.SelectSingleNode("td[6]/text()");
                var moneyNode = betInfoNode.SelectSingleNode("td[5]");
                var oddsValueNode = betInfoNode.SelectSingleNode("td[4]/span[1]");
                var typeNode = betInfoNode.SelectSingleNode("td[3]/div[2]/div[contains(@class,'TextStyle01')]");
                var priceTypeNode = betInfoNode.SelectSingleNode("td[3]/div[2]/div[contains(@class,'FavTeamClass')]");
                var scoreNode = betInfoNode.SelectSingleNode("td[3]/div[2]/span[contains(@class,'TextStyle01')]");
                var handicapNode = betInfoNode.SelectSingleNode("td[3]/div[2]/span[contains(@class,'TextStyle03')]");
                if (oddsIdNode == null) continue;

                var order = new BettingOrder();
                decimal money = 0m;
                decimal oddsValue = 0m;
                decimal handicap = 0m;
                decimal.TryParse(handicapNode != null ? handicapNode.InnerText : "0", out handicap);
                decimal.TryParse(moneyNode != null ? moneyNode.InnerText : "0", out money);
                decimal.TryParse(oddsValueNode != null ? oddsValueNode.InnerText : "0", out oddsValue);
                string idStr = oddsIdNode == null ? "" : oddsIdNode.InnerText;
                string timeStr = timeNode != null ? timeNode.InnerText : "";
                string typeStr = typeNode != null ? typeNode.InnerText.Trim() : "";
                string priceTypeStr = priceTypeNode != null ? priceTypeNode.InnerText.Trim() : "";
                string scoreStr = scoreNode != null ? scoreNode.InnerText.Trim() : "";
                string league = leagueNode != null ? leagueNode.InnerText.Trim() : "";
                string hTeam = "", vTeam = "";
                if (teamNode != null)
                {
                    var teamArrayString = teamNode.InnerText.Split(new string[] { "vs" }, StringSplitOptions.RemoveEmptyEntries);
                    if (teamArrayString.Length >= 2)
                    {
                        hTeam = teamArrayString[0].Trim('-').Trim();
                        vTeam = teamArrayString[1].Trim('-').Trim();
                    }
                }

                order.BmID = BookerID;
                order.Account = Account;
                order.RecID = idStr.Replace("Ref No:", "").Trim();
                order.RawStatus = statusNode == null ? "" : statusNode.InnerText.Trim();
                order.RawLeagueName = league;
                order.RawHomeTeam = hTeam;
                order.RawAwayTeam = vTeam;
                order.RawTypeName = typeStr;
                var matchScore = regSocreMode1.Match(scoreStr);
                byte hScore = 0, vScore = 0;
                if (matchScore.Success)
                {
                    byte.TryParse(matchScore.Groups["h"].Value, out hScore);
                    byte.TryParse(matchScore.Groups["v"].Value, out vScore);
                }
                order.RawHomeScore = hScore;
                order.RawAwayScore = vScore;
                order.RawScore = scoreStr;
                order.Money = money;
                order.Handicap = handicap;
                order.OddsValue = oddsValue;
                DateTime time;
                if (DateTime.TryParse(timeStr, out time))
                {
                    var timeZone = TimeZoneInfo.FindSystemTimeZoneById("Pacific SA Standard Time");
                    order.BettingTime = TimeZoneInfo.ConvertTime(time, timeZone, TimeZoneInfo.Local);
                }
                if (typeStr.IndexOf("標準盤") != -1)
                    order.Type = MarketType.M1X2;
                else if (typeStr.IndexOf("大小盤") != -1)
                    order.Type = MarketType.MOU;
                else if (typeStr.IndexOf("讓球") != -1)
                    order.Type = MarketType.MHDP;
                switch (order.Type)
                {
                    case MarketType.M1X2:
                        if (priceTypeStr.IndexOf("1") != -1)
                            order.PriceType = OddsPrice.OddsPriceType.Home;
                        else if (priceTypeStr.IndexOf("2") != -1)
                            order.PriceType = OddsPrice.OddsPriceType.Away;
                        else if (priceTypeStr.IndexOf("X", StringComparison.OrdinalIgnoreCase) != -1)
                            order.PriceType = OddsPrice.OddsPriceType.Tie;
                        break;
                    case MarketType.MOU:
                        if (priceTypeStr.IndexOf("大") != -1)
                            order.PriceType = OddsPrice.OddsPriceType.Over;
                        else if (priceTypeStr.IndexOf("小") != -1)
                            order.PriceType = OddsPrice.OddsPriceType.Under;
                        break;
                    case MarketType.MHDP:
                        if (priceTypeStr == hTeam)
                            order.PriceType = OddsPrice.OddsPriceType.Home;
                        else if (priceTypeStr == vTeam)
                            order.PriceType = OddsPrice.OddsPriceType.Away;
                        break;
                    default:
                        break;
                }
                orders.Add(order);
            }
            return orders;
        }

        public async override Task<IList<BettingOrder>> RequestBettingMiniOrder()
        {
            if (!ValidSignup() && ValidTodayRequest()) return null;
            long ticks = GetUnixStampTicks();
            string dataUrl = string.Format("http://{0}/BetListMini_data.aspx?showBetAcceptedMsg=no&from=mini&_={1}", Host, ticks);
            string referer = string.Format(@"http://{0}/LeftAllInOne.aspx", Host);
            var reqTask = GetHtmlRequest(new Uri(dataUrl), referer);
            var html = await reqTask;
            if (string.IsNullOrEmpty(html) || reqTask.IsCanceled || reqTask.Exception != null)
                return null;
            _loginInfo.Reset(html);
            return ResolveHtmlBettingOrder(html);
        }

        protected virtual IList<BettingOrder> ResolveHtmlBettingOrder(string html)
        {
            List<BettingOrder> orders = new List<BettingOrder>();
            HtmlDocument doc = new HtmlDocument();
            doc.LoadHtml(html);
            var prevResultNodes = doc.DocumentNode.SelectNodes("//div[contains(@class,'BetInfo')]");
            if (prevResultNodes == null) return orders;
            foreach (var resultNode in prevResultNodes)
            {
                var idNode = resultNode.SelectSingleNode("table/tr[5]/td[1]/div[1]");
                var stateNode = resultNode.SelectSingleNode("table/tr[5]/td[1]/div[2]");
                var typeNode = resultNode.SelectSingleNode("table/tr[1]/td[1]");
                var moneyNode = resultNode.SelectSingleNode("table/tr[3]/td[2]");
                var handicapNode = resultNode.SelectSingleNode("table/tr[3]/td[1]/div[1]/span[1]");
                var scoreNode = resultNode.SelectSingleNode("table/tr[3]/td[1]/div[1]/span[2]");
                var oddsNode = resultNode.SelectSingleNode("table/tr[3]/td[1]/div[1]/span[3]");
                var dirNode = resultNode.SelectSingleNode("table/tr[2]/td[1]");
                var teamsNode = resultNode.SelectSingleNode("table/tr[4]/td[1]");

                BettingOrder order = new BettingOrder();
                string idStr = idNode == null ? "" : idNode.InnerText;
                string strHandicap = handicapNode == null ? "" : handicapNode.InnerText.Trim();
                string strMoney = moneyNode != null ? "" : moneyNode.InnerText.Trim();
                string strDir = dirNode == null ? "" : dirNode.InnerText.Trim();
                string strOdds = oddsNode == null ? "" : oddsNode.InnerText.Trim();
                string strScore = scoreNode == null ? "" : scoreNode.InnerText.Trim();
                string strType = typeNode == null ? "" : typeNode.InnerText.Trim();
                string hTeam = "", vTeam = "";
                if (teamsNode != null)
                {
                    var strArr = teamsNode.InnerText.Split(new string[] { "-vs-" }, StringSplitOptions.None);
                    if (strArr.Length > 1)
                    {
                        hTeam = strArr[0];
                        vTeam = strArr[1];
                    }
                }
                decimal handicap = 0m;
                decimal odds = 0m;
                decimal money = 0m;
                byte hScore = 0, vScore = 0;
                decimal.TryParse(strHandicap, out handicap);
                decimal.TryParse(strOdds, out odds);
                decimal.TryParse(strMoney, out money);
                var matchScore = regSocreMode1.Match(strScore);
                if (matchScore.Success)
                {
                    byte.TryParse(matchScore.Groups["h"].Value, out hScore);
                    byte.TryParse(matchScore.Groups["v"].Value, out vScore);
                }

                order.BmID = BookerID;
                order.Account = Account;
                order.RecID = idStr.Replace("ID:", "").Trim();
                order.RawHomeTeam = hTeam;
                order.RawAwayTeam = vTeam;
                order.RawStatus = stateNode == null ? "" : stateNode.InnerText.Trim();
                order.RawTypeName = strType;
                order.Handicap = handicap;
                order.OddsValue = odds;
                order.Money = money;
                order.RawHomeScore = hScore;
                order.RawAwayScore = vScore;
                order.RawScore = strScore;
                if (strType.IndexOf("標準盤") != -1)
                    order.Type = MarketType.M1X2;
                else if (strType.IndexOf("大小盤") != -1)
                    order.Type = MarketType.MOU;
                else if (strType.IndexOf("讓球") != -1)
                    order.Type = MarketType.MHDP;
                switch (order.Type)
                {
                    case MarketType.M1X2:
                        if (strDir.IndexOf("1") != -1)
                            order.PriceType = OddsPrice.OddsPriceType.Home;
                        else if (strDir.IndexOf("2") != -1)
                            order.PriceType = OddsPrice.OddsPriceType.Away;
                        else if (strDir.IndexOf("X", StringComparison.OrdinalIgnoreCase) != -1)
                            order.PriceType = OddsPrice.OddsPriceType.Tie;
                        break;
                    case MarketType.MOU:
                        if (strDir.IndexOf("大") != -1)
                            order.PriceType = OddsPrice.OddsPriceType.Over;
                        else if (strDir.IndexOf("小") != -1)
                            order.PriceType = OddsPrice.OddsPriceType.Under;
                        break;
                    case MarketType.MHDP:
                        if (strDir == hTeam)
                            order.PriceType = OddsPrice.OddsPriceType.Home;
                        else if (strDir == vTeam)
                            order.PriceType = OddsPrice.OddsPriceType.Away;
                        break;
                    default:
                        break;
                }

                order.BettingTime = null;
                orders.Add(order);
            }

            return orders;
        }
        #endregion

        #region request betting
        public async override Task<BettingResult> RequestBetting(BetTick tick, decimal money)
        {
            if (!ValidSignup()) return null;
            string url = string.Format("http://{0}/underover/confirm_bet_data.aspx", Host);
            string referer = string.Format("htp://{0}/LeftAllInOne.aspx", Host);
            var gmTypeMapping = GameEventOddsBetTypeMapping.FirstOrDefault(gm => gm.Value == tick.MarketType);
            int betType = gmTypeMapping.Key;
            string rqContent = string.Format(@"BPstake={3}&HorseBPstake=&stakeRequest=&sporttype=1&username={0}&cbAcceptBetterOdds=1&oddsType=4&bettype={1}&oddsRequest={2}"
                                                        , Account, betType, tick.Price, (int)money);
            var rqUrl = new Uri(url);
            var cookies = CookiesManager.GetCookies(BookerID, Account, rqUrl);
            HttpWebRequest request = CreateRequest(rqUrl, cookies, HTML_MIME, referer, rqContent, "POST");
            request.ContentType = "application/x-www-form-urlencoded";
            request.Headers.Add("X-Requested-With", "XMLHttpRequest");
            string resContent = string.Empty;
            try
            {
                var resTask = request.GetResponseAsync();

                HttpWebResponse response = (HttpWebResponse)await resTask;

                if (resTask.Exception != null)
                    throw resTask.Exception;
                if (resTask.IsCanceled)
                    throw new Exception("发送请求被任务取消.");

                var resStream = response.GetResponseStream();
                CookiesManager.SetCookies(BookerID, Account, response.Cookies);
                var reader = new System.IO.StreamReader(resStream, Encoding);
                resContent = reader.ReadToEnd();
                resStream.Close();
                response.Close();
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                request.Abort();
            }
            _loginInfo.Reset(resContent);
            var bettingResult = ResolveBettingResult(resContent);
            if (bettingResult.State != BettingState.Succeed)
                return bettingResult;
            var order = await RequestLastBettingOrder();
            bettingResult.Order = order;
            return bettingResult;
        }

        protected async virtual Task<BettingOrder> RequestLastBettingOrder()
        {
            string url = string.Format("http://{0}/underover/successbet_data.aspx", Host);
            string referer = string.Format("htp://{0}/LeftAllInOne.aspx", Host);
            var task = PostHtmlRequest(new Uri(url), referer, null);
            var html = await task;
            _loginInfo.Reset(html);
            var match = regBettingResultOrder.Match(html);
            if (!match.Success) return null;
            var json = match.Groups["j"].Value;

            return ResolveBettingOrder(html);
        }

        protected virtual BettingOrder ResolveBettingOrder(string jsonString)
        {
            var json = (JObject)JsonConvert.DeserializeObject(jsonString);

            BettingOrder order = new BettingOrder();
            order.RecID = json.Property("lblRefnoValue").Value<string>();
            order.RawHomeTeam = json.Property("lblHome").Value<string>();
            order.RawAwayTeam = json.Property("lblAway").Value<string>();
            order.RawLeagueName = json.Property("lblLeaguename").Value<string>();
            order.RawTypeName = json.Property("lblTypeName").Value<string>();
            order.RawStatus = json.Property("lblStatusValue").Value<string>();
            order.Handicap = json.Property("lblBetKindValue").TryCaseValue<decimal>(0m);
            order.Money = json.Property("lblStakeValue").TryCaseValue<decimal>(0m);
            order.OddsValue = json.Property("lblOddsValue").TryCaseValue<decimal>(0m);
            var choice = json.Property("lblChoiceValue").Value<string>();
            if (order.RawTypeName == "讓球")
            {
                if (choice == "大")
                    order.PriceType = OddsPrice.OddsPriceType.Over;
                if (choice == "小")
                    order.PriceType = OddsPrice.OddsPriceType.Under;
            }
            else if (order.RawTypeName == "大小盤")
            {
                if (choice == order.RawHomeTeam)
                    order.PriceType = OddsPrice.OddsPriceType.Home;
                else if (choice == order.RawAwayTeam)
                    order.PriceType = OddsPrice.OddsPriceType.Away;
            }
            else if (order.RawTypeName.Contains("標準盤"))
            {
                if (choice.Contains('1'))
                    order.PriceType = OddsPrice.OddsPriceType.Home;
                if (choice.Contains('2'))
                    order.PriceType = OddsPrice.OddsPriceType.Away;
                if (choice.Contains('X'))
                    order.PriceType = OddsPrice.OddsPriceType.Tie;
            }

            return order;
        }

        protected virtual BettingResult ResolveBettingResult(string html)
        {
            BettingResult result = new BettingResult { Message = "未知", State = BettingState.Unknow };
            if (html.IndexOf("Mod='Success'") > -1)
            {
                result.State = BettingState.Succeed;
                result.Message = "进行中";
                return result;
            }
            else if (html.IndexOf("Type='suspend'") > -1)
            {
                result.State = BettingState.Suspend;
                result.Message = "暂停取消";
            }
            else if (html.IndexOf("Mod='BListMini'") > -1)
            {
                result.Message = "请转入订单查看";
            }
            return result;
        }
        #endregion

        #region request bet tick
        Dictionary<OddsPrice.OddsPriceType, string> PriceTypeMappingAlias = new Dictionary<OddsPrice.OddsPriceType, string>
        {
            {OddsPrice.OddsPriceType.Home, "h:1"},
            {OddsPrice.OddsPriceType.Away, "a:2"},
            {OddsPrice.OddsPriceType.Tie, "X"},
            {OddsPrice.OddsPriceType.Over, "h"},
            {OddsPrice.OddsPriceType.Under, "a"}
        };

        protected virtual string ResolvePriceOpValue(Odds.MarketType mType, OddsPrice.OddsPriceType pType)
        {
            string op = PriceTypeMappingAlias[pType];
            if (mType == Odds.MarketType.FH1X2 || mType == Odds.MarketType.G1X2)
                op = op[2].ToString();
            else if (mType == Odds.MarketType.FHHDP || mType == Odds.MarketType.GHDP)
                op = op[0].ToString();
            return op;
        }

        public override async Task<BetTick> RequestTick(BetTick oldTick)
        {
            if (!ValidSignup() && ValidTodayRequest()) return null;
            long ticks = GetUnixStampTicks();
            string op = ResolvePriceOpValue(oldTick.MarketType, oldTick.PriceType);
            string dataUrl = string.Format(@"http://{0}/BetProcess_Data.aspx?autoLoad=yes&bp_Match={1}_{2}_{3}&UN={4}&bp_ssport=1&chk_BettingChange=2&{6}={7}&bp_key=bet&_={5}"
                , Host, oldTick.OddsID, op, oldTick.Price, Account, ticks, FormRequestExtKey, FormRequestExtValue);

            string referer = string.Format(@"http://{0}/LeftAllInOne.aspx", Host);
            var tkReqTask = GetHtmlRequest(new Uri(dataUrl), referer);
            var html = await tkReqTask;
            if (string.IsNullOrEmpty(html) || tkReqTask.IsCanceled || tkReqTask.Exception != null)
                return null;
            _loginInfo.Reset(html);
            var matTick = regTickJson.Match(html);
            if (!matTick.Success)
                return null;
            var jsonString = matTick.Groups["j"].Value;
            var json = (JObject)JsonConvert.DeserializeObject(jsonString);
            ResolveJsonToBetTick(oldTick, json);
            oldTick.UpdateAccount = Account;
            oldTick.UpdateHost = Host;
            return oldTick;
        }

        public override async Task<BetTick> RequestTick(string oddsId, OddsPrice price)
        {
            if (!ValidSignup() && ValidTodayRequest()) return null;
            long ticks = GetUnixStampTicks();
            string op = ResolvePriceOpValue(price.MkType, price.Type);
            string dataUrl = string.Format(@"http://{0}/BetProcess_Data.aspx?bp_Match={1}_{2}_{3:N2}&UN={4}&bp_ssport=1&chk_BettingChange=2&{6}={7}&bp_key=bet&_={5}"
                            , Host, oddsId, op, price.Price, Account, ticks, FormRequestExtKey, FormRequestExtValue);

            string referer = string.Format(@"http://{0}/LeftAllInOne.aspx", Host);
            var tkReqTask = GetHtmlRequest(new Uri(dataUrl), referer);
            var html = await tkReqTask;
            if (string.IsNullOrEmpty(html) || tkReqTask.IsCanceled || tkReqTask.Exception != null)
                return null;
            _loginInfo.Reset(html);
            var matTick = regTickJson.Match(html);
            if (!matTick.Success)
                return null;
            var jsonString = matTick.Groups["j"].Value;
            var json = (JObject)JsonConvert.DeserializeObject(jsonString);
            BetTick tick = new BetTick();
            tick.UpdateAccount = Account;
            tick.UpdateHost = Host;
            tick.OddsID = oddsId;
            tick.BmID = BookerID;
            tick.MarketType = price.MkType;
            tick.PriceType = price.Type;
            ResolveJsonToBetTick(tick, json);
            return tick;
        }

        protected BetTick ResolveJsonToBetTick(BetTick tick, JObject json)
        {
            tick.Price = json["lblOddsValue"].TryCaseValue(tick.Price);
            tick.MaxAmount = json["lblMaxBetValue"].TryCaseValue(tick.MaxAmount);
            tick.MinAmount = json["lblMinBetValue"].TryCaseValue(tick.MinAmount);
            tick.HomeTeam = json["lblHome"].Value<string>();
            tick.AwayTeam = json["lblAway"].Value<string>();
            tick.Handicap = json["lblBetKindValue"].TryCaseValue(tick.Handicap);
            var scoreStr = json["lblScoreValue"].Value<string>();
            if (!string.IsNullOrEmpty(scoreStr))
            {
                var mat = regTickScore.Match(scoreStr.Trim());
                if (mat.Success)
                {
                    byte hscore, ascore;
                    byte.TryParse(mat.Groups["h"].Value, out hscore);
                    byte.TryParse(mat.Groups["a"].Value, out ascore);
                    tick.HomeScore = hscore;
                    tick.AwayScore = ascore;
                }
            }
            tick.GameID = json["matchid"].Value<string>();
            return tick;
        }
        #endregion

        #region request game event data
        public override async Task<bool> RequestUpdateLiveOdds()
        {
            if (!ValidSignup() && ValidTodayRequest()) return false;
            LiveFormParamters.RT = "U";
            LiveFormParamters.DT = "";
            var ct = LastUpdateLiveOdds.HasValue ? LastUpdateLiveOdds.Value : DateTime.Now.AddSeconds(-20);
            LiveFormParamters.CT = System.Web.HttpUtility.UrlEncode(ct.ToString("MM/dd/yyyy hh:mm:ss"));
            long ticks = GetUnixStampTicks();
            string underOverUrl = string.Format("http://{0}/UnderOver.aspx?Sport=1&Market=t&DispVer=3&Game=0", Host);
            string dataUrl = string.Format("http://{0}/UnderOver_data.aspx?{1}&_={2}"
                    , Host, LiveFormParamters.ToString(), ticks);
            var reqTask = GetHtmlRequest(new Uri(dataUrl), underOverUrl);
            var result = await reqTask;
            if (string.IsNullOrEmpty(result) || reqTask.IsCanceled || reqTask.Exception != null)
                return false;

            _loginInfo.Reset(result);

            LastUpdateLiveOdds = ResolveRegexTime(regUpdateEventTime, "d", result);

            var matDell = regLiveDell.Match(result);
            var matInsl = regLiveInsl.Matches(result);
            var matLl = regLiveuLl.Matches(result);
            var matMl = regLiveuMl.Matches(result);
            var matAl = regLiveuAl.Matches(result);
            var matOl = regLiveuOl.Matches(result);
            if (matDell.Success)
            {
                var jsonDellString = matDell.Groups["j"].Value;
                var jsonDell = (JArray)JsonConvert.DeserializeObject(jsonDellString);
                var delIds = jsonDell.Select(j => j.TryCaseValue<int>(0).ToString()).ToArray();
                if (RepositoryService != null) RepositoryService.DisableGames(BookerID, delIds);
            }

            var arrayInsl = ResolveOrderJson(matInsl, "i", "j");
            var arrayLl = ResolveOrderJson(matLl, "i", "j");
            var arrayMl = ResolveOrderJson(matMl, "i", "j");
            var arrayAl = ResolveOrderJson(matAl, "i", "j");
            var arrayOl = ResolveOrderJson(matOl, "i", "j");

            IList<Game> updateGames = new List<Game>();
            if (arrayInsl != null && arrayInsl.Count > 0)
                ResolveNewEventsJson(arrayInsl, true);

            if (arrayLl != null && arrayLl.Count > 0)
                ResolveUpdateEventState(arrayLl, updateGames, true);

            if (arrayMl != null && arrayMl.Count > 0)
                ResolveUpdateEventInfo(arrayLl, updateGames, true);

            if (arrayAl != null && arrayAl.Count > 0)
                ResolveUpdateEventAppend(arrayAl, updateGames, true);

            IList<Market> updateMarkets = new List<Market>();
            IList<Odds> updateOdds = new List<Odds>();
            if (arrayOl != null && arrayOl.Count > 0)
                ResolveUpdateOdds(arrayOl, updateGames, updateMarkets, updateOdds, true);

            if (RepositoryService != null)
            {
                if (updateGames.Count > 0)
                    RepositoryService.UpdateAllGame(updateGames);
                if (updateMarkets.Count > 0)
                    RepositoryService.UpdateAllMarket(updateMarkets);
                if (updateOdds.Count > 0)
                    RepositoryService.UpdateAllOdds(updateOdds);
            }
            return true;
        }

        public override async Task<bool> RequestUpdateTodayOdds()
        {
            if (!ValidSignup() && ValidTodayRequest()) return false;

            NoLiveFormParamters.RT = "U";
            NoLiveFormParamters.DT = "";
            NoLiveFormParamters.OrderBy = 1;
            var ct = LastUpdateNoLiveOdds.HasValue ? LastUpdateNoLiveOdds.Value : DateTime.Now.AddHours(-12).AddSeconds(-20);
            NoLiveFormParamters.CT = System.Web.HttpUtility.UrlEncode(ct.ToString("MM/dd/yyyy hh:mm:ss"));
            long ticks = GetUnixStampTicks();

            string underOverUrl = string.Format("http://{0}/UnderOver.aspx?Sport=1&Market=t&DispVer=new&Game=0", Host);
            string dataUrl = string.Format("http://{0}/UnderOver_data.aspx?{1}&_={2}"
                    , Host, LiveFormParamters.ToString(), ticks);
            var reqTask = GetHtmlRequest(new Uri(dataUrl), underOverUrl);
            var result = await reqTask;
            if (string.IsNullOrEmpty(result) || reqTask.IsCanceled || reqTask.Exception != null)
                return false;
            _loginInfo.Reset(result);

            LastUpdateNoLiveOdds = ResolveRegexTime(regUpdateEventTime, "d", result);
            var matDell = regNLiveDelt.Match(result);
            var matInsl = regNLiveInst.Matches(result);
            var matLl = regNLiveuLt.Matches(result);
            var matMl = regNLiveuMt.Matches(result);
            var matAl = regNLiveuAt.Matches(result);
            var matOl = regNLiveuOt.Matches(result);
            //if (matDell.Success)
            //{
            //    var jsonDellString = matDell.Groups["j"].Value;
            //    var jsonDell = (JArray)JsonConvert.DeserializeObject(jsonDellString);
            //    var delIds = jsonDell.Select(j => Models.Game.CreateUniqueID(BookerID, j.TryCaseValue<int>(0).ToString()));
            //    if (Repository != null) Repository.DisableGames(delIds);
            //}

            var arrayInst = ResolveOrderJson(matInsl, "i", "j");
            var arrayLt = ResolveOrderJson(matLl, "i", "j");
            var arrayMt = ResolveOrderJson(matMl, "i", "j");
            var arrayAt = ResolveOrderJson(matAl, "i", "j");
            var arrayOt = ResolveOrderJson(matOl, "i", "j");

            IList<Game> updateGames = new List<Game>();
            if (arrayInst != null && arrayInst.Count > 0)
                ResolveNewEventsJson(arrayInst, false);

            if (arrayLt != null && arrayLt.Count > 0)
                ResolveUpdateEventState(arrayLt, updateGames, false);

            if (arrayMt != null && arrayMt.Count > 0)
                ResolveUpdateEventInfo(arrayLt, updateGames, false);

            if (arrayAt != null && arrayAt.Count > 0)
                ResolveUpdateEventAppend(arrayAt, updateGames, false);

            IList<Market> updateMarkets = new List<Market>();
            IList<Odds> updateOdds = new List<Odds>();
            if (arrayOt != null && arrayOt.Count > 0)
                ResolveUpdateOdds(arrayOt, updateGames, updateMarkets, updateOdds, false);

            if (RepositoryService != null)
            {
                RepositoryService.UpdateAllGame(updateGames);
                RepositoryService.UpdateAllMarket(updateMarkets);
                RepositoryService.UpdateAllOdds(updateOdds);
            }
            return true;
        }

        public override async Task<IList<Game>> RequestLiveFullOdds()
        {
            if (!ValidSignup() && ValidTodayRequest()) return null;

            LiveFormParamters.RT = "W";
            LiveFormParamters.CT = "";
            LiveFormParamters.DT = "";
            long ticks = GetUnixStampTicks();
            string underOverUrl = string.Format("http://{0}/UnderOver.aspx?Sport=1&Market=t&DispVer=3&Game=0", Host);
            string dataUrl = string.Format("http://{0}/UnderOver_data.aspx?{1}&_={2}"
                    , Host, LiveFormParamters.ToString(), ticks);

            var reqTask = GetHtmlRequest(new Uri(dataUrl), underOverUrl);
            var result = await reqTask;

            if (string.IsNullOrEmpty(result) || reqTask.IsCanceled || reqTask.Exception != null)
                return null;
            _loginInfo.Reset(result);

            LastUpdateLiveOdds = ResolveRegexTime(regUpdateEventTime, "d", result);
            var neMatchs = regLiveNewEvents.Matches(result);
            var jsonArray = ResolveOrderJson(neMatchs, "i", "j");
            var games = ResolveNewEventsJson(jsonArray, true);
            return games;
        }

        public override async Task<IList<Game>> RequestTodayFullOdds()
        {
            if (!ValidSignup() && ValidTodayRequest()) return null;
            NoLiveFormParamters.RT = "W";
            NoLiveFormParamters.CT = "";
            NoLiveFormParamters.DT = "";
            NoLiveFormParamters.OrderBy = 1;
            long ticks = GetUnixStampTicks();
            string underOverUrl = string.Format("http://{0}/UnderOver.aspx?Sport=1&Market=t&DispVer=3&Game=0", Host);
            string dataUrl = string.Format("http://{0}/UnderOver_data.aspx?{1}&DispRang={2}&_={3}"
                    , Host, NoLiveFormParamters.ToString(), NoLiveFormParamters.DispRang, ticks);

            var reqTask = GetHtmlRequest(new Uri(dataUrl), underOverUrl);
            var result = await reqTask;

            if (string.IsNullOrEmpty(result) || reqTask.IsCanceled || reqTask.Exception != null)
                return null;
            _loginInfo.Reset(result);

            LastUpdateNoLiveOdds = ResolveRegexTime(regUpdateEventTime, "d", result);
            var neMatchs = regTodayNewEvents.Matches(result);
            var jsonArray = ResolveOrderJson(neMatchs, "i", "j");
            var games = ResolveNewEventsJson(jsonArray, false);
            return games;
        }

        public override async Task<IList<Game>> RequestEarlyMarketOdds()
        {
            if (!ValidSignup()) return null;

            long ticks = GetUnixStampTicks();
            string leftUrl = string.Format("http://{0}/LeftAllInOne.aspx", Host);
            string underOverUrl = string.Format("http://{0}/UnderOver.aspx?Sport=1&Market=e&DispVer=3&Game=0", Host);

            var uoreqTask = GetHtmlRequest(new Uri(underOverUrl), leftUrl);
            var html = await uoreqTask;
            ResolveEarlyFormParamters(html);
            EarlyFormParamters.DT = "";
            EarlyFormParamters.CT = System.Web.HttpUtility.UrlEncode(DateTime.Now.AddHours(-12).ToString("MM/dd/yyyy hh:mm:ss"));
            string dataUrl = string.Format("http://{0}/UnderOver_data.aspx?{1}&DispRang={2}&_={3}"
                , Host, EarlyFormParamters.ToString(), EarlyFormParamters.DispRang, ticks);
            ///UnderOver_data.aspx?Market=e&Sport=1&DT=&RT=W&CT=04%2F01%2F2014+23%3A27%3A01&Game=0&OrderBy=0&OddsType=4&MainLeague=1&DispRang=0&k426617242=v426617569&key=dodds&_=1396409322527 
            var reqTask = GetHtmlRequest(new Uri(dataUrl), underOverUrl);
            var result = await reqTask;

            if (string.IsNullOrEmpty(result) || reqTask.IsCanceled || reqTask.Exception != null)
                return null;
            _loginInfo.Reset(result);

            var neMatchs = regNewEvents.Matches(result);
            var jsonArray = ResolveOrderJson(neMatchs, "i", "j");
            var games = ResolveNewEventsJson(jsonArray, false);
            return games;
        }
        #endregion

        protected virtual IList<string> ResolveOrderJson(MatchCollection matchRange, string indexKey, string jsonKey)
        {
            if (matchRange.Count == 0) return null;
            Dictionary<int, string> jsonStringDic = new Dictionary<int, string>();
            foreach (Match mat in matchRange)
            {
                var json = mat.Groups[jsonKey].Value;
                int index = 0;
                int.TryParse(mat.Groups[indexKey].Value, out index);
                if (!jsonStringDic.ContainsKey(index))
                {
                    jsonStringDic.Add(index, json);
                }
            }
            var jsonArray = jsonStringDic.OrderBy(j => j.Key).Select(j => j.Value).ToList();
            return jsonArray;
        }

        #region resolve json update events
        protected virtual bool ResolveUpdateEventState(IList<string> jsonStringArray, IList<Game> updateGames, bool isLive)
        {
            if (RepositoryService == null) return false;

            foreach (var jsonString in jsonStringArray)
            {
                var json = (JArray)JsonConvert.DeserializeObject(jsonString);
                var mapping = GameEventLiveStateMapping;
                var gid = MappingJsonValue(json, mapping, "MUID", 0);
                var game = updateGames.FirstOrDefault(g => g.GameID == gid.ToString());
                if (game == null)
                {
                    game = RepositoryService.GetGame(BookerID, gid.ToString());
                    if (game == null) continue;
                    updateGames.Add(game);
                }
                var showTime = MappingJsonValue(json, mapping, "ShowTime", "");
                var csStatus = MappingJsonValue(json, mapping, "CsStatus", "");
                var matTime = regShowTime.Match(showTime);
                game.PlayeTime = showTime;
                game.HomeScore = MappingJsonValue(json, mapping, "ScoreH", game.HomeScore);
                game.AwayScore = MappingJsonValue(json, mapping, "ScoreA", game.AwayScore);
                game.InjuryMinutes = MappingJsonValue(json, mapping, "InjuryTime", game.InjuryMinutes);
                game.Pried = MappingJsonValue(json, mapping, "LivePeriod", game.Pried);
                game.LastUpdateTime = DateTime.Now;
                if (matTime.Success)
                {
                    byte min;
                    if (byte.TryParse(matTime.Groups["m"].Value, out min))
                        game.PlayMinutes = min;
                }
                if (isLive)
                {
                    if (game.State == Game.GameState.PreGame)
                        RepositoryService.UpdatePreGameOdds(BookerID, game.GameID);
                    game.State = Game.GameState.InProgress;
                }
            }

            return true;
        }
        protected virtual bool ResolveUpdateEventInfo(IList<string> jsonStringArray, IList<Game> updateGames, bool isLive)
        {
            if (RepositoryService == null) return false;

            foreach (var jsonString in jsonStringArray)
            {
                try
                {
                    var json = (JArray)JsonConvert.DeserializeObject(jsonString);
                    var mapping = GameEventBaseInfoMapping;
                    var gid = MappingJsonValue(json, mapping, "MUID", 0);
                    var game = updateGames.FirstOrDefault(g => g.GameID == gid.ToString());
                    if (game == null)
                    {
                        game = RepositoryService.GetGame(BookerID, gid.ToString());
                        if (game == null) continue;
                        updateGames.Add(game);
                    }

                    var showTime = MappingJsonValue(json, mapping, "ShowTime", "");
                    var kickoffTime = json.Count > 8 ? MappingJsonValue(json, mapping, "KickoffTime", "") : "";
                    var matTime = regShowTime.Match(showTime);
                    game.PlayeTime = showTime;
                    game.HomeTeam = MappingJsonValue(json, mapping, "HomeName", game.HomeTeam);
                    game.AwayTeam = MappingJsonValue(json, mapping, "AwayName", game.AwayTeam);
                    var startTime = ResolveKickoffTime(kickoffTime);
                    game.StartTime = startTime.HasValue ? startTime : game.StartTime;
                    game.LastUpdateTime = DateTime.Now;
                    if (matTime.Success)
                    {
                        byte min;
                        if (byte.TryParse(matTime.Groups["m"].Value, out min))
                            game.PlayMinutes = min;
                    }
                    if (isLive)
                    {
                        if (game.State == Game.GameState.PreGame)
                            RepositoryService.UpdatePreGameOdds(BookerID, game.GameID);
                        game.State = Game.GameState.InProgress;
                    }
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }

            return true;
        }
        protected virtual bool ResolveUpdateEventAppend(IList<string> jsonStringArray, IList<Game> updateGames, bool isLive)
        {
            if (RepositoryService == null) return false;

            foreach (var jsonString in jsonStringArray)
            {
                var json = (JArray)JsonConvert.DeserializeObject(jsonString);
                var mapping = GameEventAppendMapping;
                var gid = MappingJsonValue(json, mapping, "MUID", 0);
                var game = updateGames.FirstOrDefault(g => g.GameID == gid.ToString());
                if (game == null)
                {
                    game = RepositoryService.GetGame(BookerID, gid.ToString());
                    if (game == null) continue;
                    updateGames.Add(game);
                }
                game.HomeRed = MappingJsonValue(json, mapping, "RedCardH", game.HomeRed);
                game.AwayRed = MappingJsonValue(json, mapping, "RedCardA", game.AwayRed);
                game.LastUpdateTime = DateTime.Now;
                if (isLive)
                {
                    if (game.State == Game.GameState.PreGame)
                        RepositoryService.UpdatePreGameOdds(BookerID, game.GameID);
                    game.State = Game.GameState.InProgress;
                }
            }

            return true;
        }

        protected virtual bool ResolveUpdateOdds(IList<string> jsonStringArray, IList<Game> updateGames, IList<Market> updateMarkets, IList<Odds> updateOdds, bool isLive)
        {
            if (RepositoryService == null) return false;

            foreach (var jsonString in jsonStringArray)
            {
                var json = (JArray)JsonConvert.DeserializeObject(jsonString);

                var gid = json[0].TryCaseValue<int>(0);
                var betType = json[1].TryCaseValue<int>(0);
                var oddsId = json[2].TryCaseValue<int>(0);
                if (gid == 0 || betType == 0 || oddsId == 0) continue;
                if (!GameEventOddsBetTypeMapping.ContainsKey(betType)) continue;

                var odds = RepositoryService.GetOdds(BookerID, oddsId.ToString());
                var marekt = RepositoryService.GetMarket(BookerID, gid.ToString(), Host);
                if (odds == null) continue;

                var game = updateGames.FirstOrDefault(g => g.GameID == gid.ToString());
                if (game == null)
                {
                    game = RepositoryService.GetGame(BookerID, gid.ToString());
                    if (game != null)
                    {
                        updateGames.Add(game);
                        game.LastUpdateHost = Host;
                        game.LastUpdateTime = DateTime.Now;
                    }
                }
                if (marekt != null)
                {
                    marekt.UpdateAccount = Account;
                    marekt.UpdateTime = DateTime.Now;
                    if (!marekt.OddsIDS.Contains(odds.OddsID))
                        marekt.OddsIDS.Add(odds.OddsID);
                    updateMarkets.Add(marekt);
                }

                var type = GameEventOddsBetTypeMapping[betType];
                if (odds.Type != type)
                    continue;
                switch (type)
                {
                    case Odds.MarketType.G1X2:
                    case Odds.MarketType.FH1X2:
                        ResolveUpdate1X2Json(json, odds, isLive);
                        break;
                    case Odds.MarketType.GHDP:
                    case Odds.MarketType.FHHDP:
                        ResolveUpdateHdpJson(json, odds, isLive);
                        break;
                    case Odds.MarketType.GOU:
                    case Odds.MarketType.FHOU:
                        ResolveUpdateOUJson(json, odds, isLive);
                        break;
                    default:
                        break;
                }
                updateOdds.Add(odds);
            }
            return true;
        }
        #endregion

        #region resolve json to new events
        protected virtual IList<Game> ResolveNewEventsJson(IList<string> jsonArray, bool isLive)
        {
            if (jsonArray == null || jsonArray.Count == 0) return null;
            List<Game> games = new List<Game>();
            List<Market> markets = new List<Market>();
            List<Odds> oddsRange = new List<Odds>();
            Tournament tournament = null;
            foreach (var jsonStr in jsonArray)
            {
                try
                {
                    var json = (JArray)JsonConvert.DeserializeObject(jsonStr);
                    var currTour = ResolveNewTournament(json);
                    if (currTour != null) tournament = currTour;
                    var gid = EventJsonValue(json, "MatchId", "");
                    if (string.IsNullOrEmpty(gid)) continue;

                    var game = games.FirstOrDefault(g => g.GameID == gid);
                    var market = markets.FirstOrDefault(m => m.GameID == gid);
                    if (game == null)
                    {
                        game = ResolveNewGameJson(json, gid);
                        market = ResolveNewMarket(game);
                        if (tournament != null)
                        {
                            game.TournamentID = tournament.ID.ToString();
                            game.Tournament = tournament.Name;
                        }
                        games.Add(game);
                        markets.Add(market);

                        if (isLive)
                        {
                            game.State = Game.GameState.InProgress;
                            if (RepositoryService != null)
                                RepositoryService.UpdatePreGameOdds(BookerID, game.GameID);
                        }
                        else
                            game.State = Game.GameState.PreGame;

                        ResolveOddsJson(json, Odds.MarketType.FH1X2, market, oddsRange, isLive);
                        ResolveOddsJson(json, Odds.MarketType.G1X2, market, oddsRange, isLive);
                        ResolveOddsJson(json, Odds.MarketType.FHHDP, market, oddsRange, isLive);
                        ResolveOddsJson(json, Odds.MarketType.GHDP, market, oddsRange, isLive);
                        ResolveOddsJson(json, Odds.MarketType.FHOU, market, oddsRange, isLive);
                        ResolveOddsJson(json, Odds.MarketType.GOU, market, oddsRange, isLive);
                    }
                    else
                    {
                        ResolveOddsJson(json, Odds.MarketType.FH1X2, market, oddsRange, isLive);
                        ResolveOddsJson(json, Odds.MarketType.G1X2, market, oddsRange, isLive);
                        ResolveOddsJson(json, Odds.MarketType.FHHDP, market, oddsRange, isLive);
                        ResolveOddsJson(json, Odds.MarketType.GHDP, market, oddsRange, isLive);
                        ResolveOddsJson(json, Odds.MarketType.FHOU, market, oddsRange, isLive);
                        ResolveOddsJson(json, Odds.MarketType.GOU, market, oddsRange, isLive);
                    }
                    game.AppendRawJson(jsonStr);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
            if (RepositoryService != null) RepositoryService.UpdateRelatedGame(games, markets, oddsRange);
            return games;
        }

        protected virtual Tournament ResolveNewTournament(JArray json)
        {
            int tourId = EventJsonValue<int>(json, "LeagueId", 0);
            if (tourId == 0) return null;
            var name = EventJsonValue(json, "LeagueName", "");
            return new Tournament(tourId, name);
        }

        protected virtual Market ResolveNewMarket(Game game)
        {
            var market = new Market();
            market.GameID = game.GameID;
            market.BmID = BookerID;
            market.Host = Host;
            market.UpdateAccount = Account;
            market.UpdateTime = DateTime.Now;
            return market;
        }

        protected virtual Game ResolveNewGameJson(JArray json, string gid)
        {
            var game = new Game();
            game.BmID = BookerID;
            game.GameID = gid;
            game.GameCode = EventJsonValue(json, "MUID", "");
            game.HomeTeam = EventJsonValue(json, "HomeName", "");
            game.AwayTeam = EventJsonValue(json, "AwayName", "");
            game.HomeScore = EventJsonValue<byte>(json, "ScoreH", 0);
            game.AwayScore = EventJsonValue<byte>(json, "ScoreA", 0);
            game.HomeRed = EventJsonValue<byte>(json, "RedCardH", 0);
            game.AwayRed = EventJsonValue<byte>(json, "RedCardA", 0);
            var showTime = EventJsonValue(json, "ShowTime", "").Trim();
            game.PlayeTime = showTime;
            var kickoffTime = EventJsonValue(json, "KickoffTime", "");
            var stMat = regShowTime.Match(showTime);
            if (stMat.Success)
            {
                byte pried = 0;
                byte gameMinutes = 0;
                byte.TryParse(stMat.Groups["p"].Value, out pried);
                byte.TryParse(stMat.Groups["m"].Value, out gameMinutes);
                game.Pried = pried;
                game.PlayMinutes = gameMinutes;
            }

            game.StartTime = ResolveKickoffTime(kickoffTime);
            game.LastUpdateTime = DateTime.Now;
            game.LastUpdateHost = Host;
            return game;
        }
        #endregion

        #region resolve odds json
        protected virtual void ResolveOddsJson(JArray json, Odds.MarketType marketType, Market addToMarket, List<Odds> addToOdds, bool isLive)
        {
            if (!GameEventOddsIndexMapping.ContainsKey(marketType))
                return;
            var starIndex = GameEventOddsIndexMapping[marketType];
            switch (marketType)
            {
                case Odds.MarketType.G1X2:
                case Odds.MarketType.FH1X2:
                    ResolveNew1X2Json(json, marketType, starIndex, addToMarket, addToOdds, isLive);
                    break;
                case Odds.MarketType.GHDP:
                case Odds.MarketType.FHHDP:
                    ResolveNewHDPJson(json, marketType, starIndex, addToMarket, addToOdds, isLive);
                    break;
                case Odds.MarketType.GOU:
                case Odds.MarketType.FHOU:
                    ResolveNewOUJson(json, marketType, starIndex, addToMarket, addToOdds, isLive);
                    break;
                default:
                    break;
            }
        }

        protected virtual void ResolveUpdate1X2Json(JArray json, Odds odds, bool isLive)
        {
            var maping = Odds1X2IndexLiveMapping;
            odds.LastUpdateTime = DateTime.Now;
            odds.LastUpdateAccount = Account;
            odds.IsLive = isLive;
            //odds.Handicap = JsonMappingValue(json, maping, "Handicap", odds.Handicap);
            var check = CheckUpdateOddsValue(json, 2, maping["Home"], maping["Tie"], maping["Away"]);
            if (!check)
            {
                odds.IsAvailable = false;
                return;
            }
            odds.IsAvailable = true;
            var priceHome = odds.Prices.FirstOrDefault(p => p.Type == OddsPrice.OddsPriceType.Home);
            var priceAway = odds.Prices.FirstOrDefault(p => p.Type == OddsPrice.OddsPriceType.Away);
            var priceTie = odds.Prices.FirstOrDefault(p => p.Type == OddsPrice.OddsPriceType.Tie);
            var priceHV = EventOddsJsonValue(json, maping, 2, "Home", 0m);
            var priceAV = EventOddsJsonValue(json, maping, 2, "Tie", 0m);
            var priceTV = EventOddsJsonValue(json, maping, 2, "Away", 0m);
            if (priceHome == null)
                odds.Prices.Add(new OddsPrice(priceHV, OddsPrice.OddsPriceType.Home));
            else
                priceHome.Price = priceHV;
            if (priceAway == null)
                odds.Prices.Add(new OddsPrice(priceAV, OddsPrice.OddsPriceType.Away));
            else
                priceAway.Price = priceAV;
            if (priceTie == null)
                odds.Prices.Add(new OddsPrice(priceTV, OddsPrice.OddsPriceType.Tie));
            else
                priceTie.Price = priceTV;
        }

        protected virtual void ResolveUpdateHdpJson(JArray json, Odds odds, bool isLive)
        {
            var maping = OddsHDPIndexMapping;
            odds.LastUpdateTime = DateTime.Now;
            odds.LastUpdateAccount = Account;
            odds.IsLive = isLive;
            var check = CheckUpdateOddsValue(json, 2, maping["Home"], maping["Away"]);
            if (!check)
            {
                odds.IsAvailable = false;
                return;
            }
            odds.IsAvailable = true;
            var priceHome = odds.Prices.FirstOrDefault(p => p.Type == OddsPrice.OddsPriceType.Home);
            var priceAway = odds.Prices.FirstOrDefault(p => p.Type == OddsPrice.OddsPriceType.Away);
            var priceHV = EventOddsJsonValue(json, maping, 2, "Home", 0m);
            var priceAV = EventOddsJsonValue(json, maping, 2, "Away", 0m);
            if (priceHome == null)
                odds.Prices.Add(new OddsPrice(priceHV, OddsPrice.OddsPriceType.Home));
            else
                priceHome.Price = priceHV;
            if (priceAway == null)
                odds.Prices.Add(new OddsPrice(priceAV, OddsPrice.OddsPriceType.Away));
            else
                priceAway.Price = priceAV;
            var focus = EventOddsJsonValue(json, maping, 2, "Focus", "");
            var handicap = EventOddsJsonValue(json, maping, 2, "Handicap", odds.Handicap.ToString());
            if (!string.IsNullOrEmpty(handicap))
                odds.Handicap = ResolveHandicap(handicap, focus);
        }

        protected virtual void ResolveUpdateOUJson(JArray json, Odds odds, bool isLive)
        {
            var maping = OddsOUIndexMapping;
            odds.LastUpdateTime = DateTime.Now;
            odds.LastUpdateAccount = Account;
            odds.IsLive = isLive;
            var check = CheckUpdateOddsValue(json, 2, maping["Over"], maping["Under"]);
            if (!check)
            {
                odds.IsAvailable = false;
                return;
            }
            odds.IsAvailable = true;
            var priceHome = odds.Prices.FirstOrDefault(p => p.Type == OddsPrice.OddsPriceType.Over);
            var priceAway = odds.Prices.FirstOrDefault(p => p.Type == OddsPrice.OddsPriceType.Under);
            var priceHV = EventOddsJsonValue(json, maping, 2, "Over", 0m);
            var priceAV = EventOddsJsonValue(json, maping, 2, "Under", 0m);
            odds.Handicap = EventOddsJsonValue(json, maping, 2, "Handicap", odds.Handicap);
            if (priceHome == null)
                odds.Prices.Add(new OddsPrice(priceHV, OddsPrice.OddsPriceType.Over));
            else
                priceHome.Price = priceHV;
            if (priceAway == null)
                odds.Prices.Add(new OddsPrice(priceAV, OddsPrice.OddsPriceType.Under));
            else
                priceAway.Price = priceAV;

            var handicap = EventOddsJsonValue(json, maping, 2, "Handicap", odds.Handicap.ToString());
            if (!string.IsNullOrEmpty(handicap))
                odds.Handicap = ResolveHandicap(handicap, "h");
        }


        protected virtual bool CheckUpdateOddsValue(JArray json, uint start, params int[] index)
        {
            foreach (var i in index)
            {
                try
                {
                    if (!json[i + (int)start].CanCast<decimal>())
                        return false;
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
            return true;
        }

        protected virtual void ResolveNew1X2Json(JArray json, Odds.MarketType marketType, int starIndex, Market addToMarket, List<Odds> addToOdds, bool isLive)
        {
            var oddsId = EventOddsJsonValue(json, Odds1X2IndexMapping, starIndex, "OddsId", "");
            if (!string.IsNullOrEmpty(oddsId))
            {
                var odds = new Odds(oddsId, marketType);
                odds.IsLive = isLive;
                odds.BmID = BookerID;
                odds.GameID = addToMarket.GameID;
                odds.LastUpdateTime = DateTime.Now;
                odds.LastUpdateAccount = Account;
                var homePrice = EventOddsJsonValue<decimal>(json, Odds1X2IndexMapping, starIndex, "Home", 0m);
                var awayPrice = EventOddsJsonValue<decimal>(json, Odds1X2IndexMapping, starIndex, "Away", 0m);
                var tiePrice = EventOddsJsonValue<decimal>(json, Odds1X2IndexMapping, starIndex, "Tie", 0m);
                odds.Prices.Add(new OddsPrice(homePrice, OddsPrice.OddsPriceType.Home));
                odds.Prices.Add(new OddsPrice(awayPrice, OddsPrice.OddsPriceType.Away));
                odds.Prices.Add(new OddsPrice(tiePrice, OddsPrice.OddsPriceType.Tie));
                addToMarket.OddsIDS.Add(odds.OddsID);
                addToOdds.Add(odds);
            }
        }

        protected virtual void ResolveNewHDPJson(JArray json, Odds.MarketType marketType, int starIndex, Market addToMarket, List<Odds> addToOdds, bool isLive)
        {
            var oddsId = EventOddsJsonValue(json, OddsHDPIndexMapping, starIndex, "OddsId", "");
            if (!string.IsNullOrEmpty(oddsId))
            {
                var odds = new Odds(oddsId, marketType);
                odds.IsLive = isLive;
                odds.BmID = BookerID;
                odds.GameID = addToMarket.GameID;
                odds.LastUpdateTime = DateTime.Now;
                odds.LastUpdateAccount = Account;
                var handicapStr = EventOddsJsonValue(json, OddsHDPIndexMapping, starIndex, "Handicap", "");
                var focus = EventOddsJsonValue(json, OddsHDPIndexMapping, starIndex, "Focus", "");
                var homePrice = EventOddsJsonValue<decimal>(json, OddsHDPIndexMapping, starIndex, "Home", 0m);
                var awayPrice = EventOddsJsonValue<decimal>(json, OddsHDPIndexMapping, starIndex, "Away", 0m);
                odds.Prices.Add(new OddsPrice(homePrice, OddsPrice.OddsPriceType.Home));
                odds.Prices.Add(new OddsPrice(awayPrice, OddsPrice.OddsPriceType.Away));
                decimal handicap = ResolveHandicap(handicapStr, focus);
                odds.Handicap = handicap;
                addToMarket.OddsIDS.Add(odds.OddsID);
                addToOdds.Add(odds);
            }
        }

        protected virtual void ResolveNewOUJson(JArray json, Odds.MarketType marketType, int starIndex, Market addToMarket, List<Odds> addToOdds, bool isLive)
        {
            var oddsId = EventOddsJsonValue(json, OddsOUIndexMapping, starIndex, "OddsId", "");
            if (!string.IsNullOrEmpty(oddsId))
            {
                var odds = new Odds(oddsId, marketType);
                odds.IsLive = isLive;
                odds.BmID = BookerID;
                odds.GameID = addToMarket.GameID;
                odds.LastUpdateTime = DateTime.Now;
                odds.LastUpdateAccount = Account;
                var handicapStr = EventOddsJsonValue(json, OddsOUIndexMapping, starIndex, "Handicap", "");

                var overPrice = EventOddsJsonValue<decimal>(json, OddsOUIndexMapping, starIndex, "Over", 0m);
                var underPrice = EventOddsJsonValue<decimal>(json, OddsOUIndexMapping, starIndex, "Under", 0m);
                odds.Prices.Add(new OddsPrice(overPrice, OddsPrice.OddsPriceType.Over));
                odds.Prices.Add(new OddsPrice(underPrice, OddsPrice.OddsPriceType.Under));
                decimal handicap = ResolveHandicap(handicapStr, "h");
                odds.Handicap = handicap;
                addToMarket.OddsIDS.Add(odds.OddsID);
                addToOdds.Add(odds);
            }
        }
        #endregion

    }
}
